﻿using System;
using ValidationEngine.Commons;
using ValidationEngine.Commons.Enums;
using ValidationEngine.Commons.Interfaces;
using ValidationEngine.Tools;

namespace ValidationEngine.ValidationRules
{
    public class CompareNumValueRule : BaseValidationRule
    {
        #region Members
        private readonly CompareOperationType _opType;
        private readonly decimal _valueToCompareWith;
        #endregion

        #region Ctor
        public CompareNumValueRule(
           object etalonVal,
            CompareOperationType opType,
           string defErrorText = null,
           RuleApplingContext applyCtx = RuleApplingContext.Always,
           ValidationErrorType errorType = ValidationErrorType.Error)
        {
            this._valueToCompareWith = TryToConvertToDecimal(etalonVal);
            this._opType = opType;
            this.ApplingContext = applyCtx;
            this._defErrorType = errorType;
        } 
        #endregion

        #region Tools
        /// <summary>
        /// Converts value to decimal
        /// </summary>
        /// <param name="value">Value to convert</param>
        /// <returns>Decimal representation</returns>
        private decimal TryToConvertToDecimal(object value)
        {
            decimal res = decimal.Zero;
            try
            {
                res = Convert.ToDecimal(value);
            }
            catch (Exception)
            {
                _brokenRule = true;
                _brokenReason += "etalonValue can not be converted to decimal";
            }
            return res;
        } 
        #endregion

        protected override IValidationInfo ApplyRule(IValidationContext ctx)
        {
            decimal? modelValue = ModelHelper.GetDecimal(ctx);
            if (!modelValue.HasValue)
            {
                return Result;
            }
            switch (_opType)
            {
                case CompareOperationType.Equel:
                    DefResult = _valueToCompareWith == modelValue.Value ? "Should not equels " + _valueToCompareWith : null;
                    break;
                case CompareOperationType.NotEquel:
                    DefResult = _valueToCompareWith != modelValue.Value ? "Should equels " + _valueToCompareWith : null;
                    break;
                case CompareOperationType.GreaterOrEquel:
                    DefResult = _valueToCompareWith < modelValue.Value ? "Should be less of equels: " + _valueToCompareWith : null;
                    break;
                case CompareOperationType.LessOrEquel:
                    DefResult = _valueToCompareWith > modelValue.Value ? "Should be greate or equels: " + _valueToCompareWith : null;
                    break;
                case CompareOperationType.LessThan:
                    DefResult = _valueToCompareWith >= modelValue.Value ? "Should be greate then: " + _valueToCompareWith : null;
                    break;
                case CompareOperationType.GreaterThan:
                    DefResult = _valueToCompareWith <= modelValue.Value ? "Should be less than: " + _valueToCompareWith : null;
                    break;
                default:
                    break;
            }
            return Result;
        }
    }

    /// <summary>
    /// Comparing operation type for CompareNumValueRule
    /// </summary>
    public enum CompareOperationType
    {
        Equel,
        NotEquel,
        GreaterThan,
        LessThan,
        GreaterOrEquel,
        LessOrEquel
    }
}
